
#include "../include/lurpc/util/ring_buffer.h"

#include <utest/utest.h>

#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>


TEST(ring_buffer_test, constructor, true) {
    lurpc::ring_buffer<int> rb;
    throw std::runtime_error("Ahh!!! Got an error!!");
}

TEST(ring_buffer_test, push, true) {
    lurpc::ring_buffer<int> rb(3);
    rb.push(31231);
    rb.push(6565);
    std::cout << rb.back() << std::endl;
    std::cout << rb.size() << std::endl;
    std::cout << rb.capacity() << std::endl;
    rb.pop();
    std::cout << rb.size() << std::endl;
    std::cout << rb.capacity() << std::endl;
    std::cout << rb.poll() << std::endl;
    std::cout << rb.size() << std::endl;
    std::cout << rb.capacity() << std::endl;
}

TEST(ring_buffer_test, benchmark, true) {
    auto t1 = std::chrono::high_resolution_clock::now();
    lurpc::ring_buffer<int> rb(1000000);
    auto t2 = std::chrono::high_resolution_clock::now();
    std::cout << "init ring buffer with cap 100000 in cost: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count()
              << std::endl;

    auto t3 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; i++) {
        rb.push(i);
    }
    auto t4 = std::chrono::high_resolution_clock::now();
    std::cout << "push ring buffer 1000000 times in cost: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(t4 - t3).count()
              << std::endl;

    auto t5 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; i++) {
        rb.pop();
    }
    auto t6 = std::chrono::high_resolution_clock::now();
    std::cout << "pop ring buffer 1000000 times in cost: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(t6 - t5).count()
              << std::endl;
}

TEST(ring_buffer_test, benchmark_cmp_vec, true) {
    std::shared_mutex sm1;
    int j = 0;
    std::vector<int *> a;
    a.resize(1000000);
    auto t1 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; i++) {
        sm1.lock();
        if (j < 0) {
            sm1.unlock();
            throw std::runtime_error("hahahaha");
        }
        a[i] = new int(std::move(i));
        j = (j + 1) % 1000000;
        sm1.unlock();
    }
    auto t2 = std::chrono::high_resolution_clock::now();
    std::cout << "compare to resized vector: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count()
              << std::endl;

    for (int i = 0; i < 1000000; i++) delete a[i];
}

TEST(ring_buffer_test, multi_thread, true) {
    lurpc::ring_buffer<int> rb(1000);
    std::vector<std::thread> ts;
    for (int i = 0; i < 20; i++) {
        ts.emplace_back([&rb]() {
            for (int i = 0; i < 20; i++) rb.push(i);
            std::cout << "[" << std::this_thread::get_id() << "]"
                      << " size: " << rb.size()
                      << " back: " << rb.back()
                      << std::endl;
            std::cout << "[" << std::this_thread::get_id() << "]"
                      << " poll: " << rb.poll()
                      << std::endl;
        });
    }
    for (int i = 0; i < 20; i++) {
        ts[i].join();
    }

    std::cout << "[MAIN] size: " << rb.size() << std::endl;

    while (rb.size() > 0) {
        std::cout << "[MAIN]"
                  << " poll: " << rb.poll()
                  << std::endl;
    }
}

TEST(ring_buffer_test, multi_thread_benchmark, true) {
    lurpc::ring_buffer<int> rb(50000);
    std::vector<std::thread> ts1;
    std::vector<std::thread> ts2;
    for (int i = 0; i < 5; i++) {
        ts1.emplace_back([&rb]() {
            for (int j = 0; j < 10000; j++) rb.push(j);
        });
        ts2.emplace_back([&rb]() {
            for (int j = 0; j < 10000; j++) rb.poll();
        });
    }
    for (int i = 0; i < 5; i++) {
        ts1[i].join();
        ts2[i].join();
    }
    std::cout << "end. buffer size: " << rb.size() << std::endl;
}

TEST_MAIN
